//: Playground - noun: a place where people can play

import UIKit

//: # swfit 读书笔记

//: ## 错误处理
//: 错误用符合Error协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。
enum VendingMachineError: Error {
    case invalidSelection                     //选择无效
    case insufficientFunds(coinsNeeded: Int) //金额不足
    case outOfStock                             //缺货
}

//: ### 用 throwing 函数传递错误
//: throws 这个标识 这个函数能抛出异常
//: rethrow  
//: 这个异常最多是抛到 调用他的方法作用域
func canThrowError() throws ->String {
    return "哈哈"
}

func canThrowErrorTwo() throws{
    
}

//: ### 处理错误
struct Item{
    var price:Int
    var count:Int
}

class VendIngMachine{
    var inventory = ["Candy Bar":Item (price: 12, count: 4),"Chips":Item (price: 13, count: 5),"Pretzels":Item (price: 14, count: 6)]
    
    var coinsDeposited = 0
    
    func dispenseSnack(snack:String) {
        print("dispense - \(snack)")
    }
    
    func vend(itemName name:String) throws {
        guard let item = inventory[name] else{
            throw VendingMachineError.invalidSelection
        }
        
        guard item.count > 0 else {
            throw VendingMachineError.outOfStock
        }
        
        guard item.price <= coinsDeposited else {
            throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
        }
        
        coinsDeposited -= item.price
        
        var newItem = item
        newItem.count -= 1
        inventory[name] = newItem;
        
         print("Dispensing \(name)")
    }
    
}

let favoriteSnacks = ["小猫":"鱼","小狗":"骨头","小马":"草","人":"Chips"]

func buyFavoriteSnack(animal:String, vendIngMachine:VendIngMachine) throws {
    
    let snackName = favoriteSnacks[animal] ?? "Candy Bar"
    //: 这个方法能抛出异常 前面用 try
    try vendIngMachine.vend(itemName: snackName)
    
}

//: ### throwing构造器能像throwing函数一样传递错误
struct PurchasedSnack{
    var name:String
    
    init(name:String,vendIngMachine:VendIngMachine) throws {
        try vendIngMachine.vend(itemName: name)
        self.name = name
    }
    
}

//: ### 用 Do-Catch 处理错误
var vendIngMachine = VendIngMachine()
vendIngMachine.coinsDeposited = 6

do {
    try buyFavoriteSnack(animal: "人", vendIngMachine: vendIngMachine)
} catch VendingMachineError.invalidSelection {
    print("老子没这玩意")
} catch VendingMachineError.insufficientFunds(let coinsNeeded){
    print("没钱别装逼 还差\(coinsNeeded)元")
} catch VendingMachineError.outOfStock{
    print("老子没货了")
}

//: ### 将错误转换成可选值
//: 使用try?通过将错误转换成一个可选值来处理错误

func throwSomeIntNum()throws -> Int{
    return 0
}

let x = try? throwSomeIntNum()

let y:Int?

do {
     y = try throwSomeIntNum()
} catch {
    y = -1
}

//: note: 上面的 x和 y 的返回值 都是 int? ，所以 x 和 y 是等价的

/*
func fetchData() -> Data? {
    if let data = try? fetchDataFromDisk() { return data }
    if let data = try? fetchDataFromServer() { return data }
    return nil
}
*/

//: ### 禁用错误传递
//: 表达式前面写try!来禁用错误传递,如果真有错抛出，提示系统的


//: ### 指定清理操作
//: 使用defer语句在即将离开当前"代码块"时执行一系列语句，该语句让你能执行一些必要的清理工作

func deferFunc(){
    defer {
        print("这个代码块 执行完调我")
    }
    
}
